/*
 * Operation.hpp
 *
 *  Created on: Nov 11, 2012
 *      Author: work
 */

#ifndef OPERATION_HPP_
#define OPERATION_HPP_

#include "Error.h"
#include "IoService.h"
#include "SocketsOps.h"

namespace zeus
{

class Operation
{
public:
	Operation()
	{

	}
	virtual ~Operation()
	{

	}

	virtual bool perform(Error & err,size_t & bytes_transferred) = 0;

	virtual void complete(Error err,size_t bytes_transferred) = 0;
};

template<typename Descriptor,typename SocketType,typename CompleteHandler>
class AcceptOperation:public Operation
{
private:
	Descriptor _descriptor;
	SocketType & _socket;
	CompleteHandler _handler;

public:
	AcceptOperation(Descriptor descriptor,SocketType & socket,const CompleteHandler & handler)
	:_descriptor(descriptor),_socket(socket),_handler(handler)
	{

	}

	bool perform(Error & err,size_t & bytes_transferred)
	{
		if(err.is_err() == true)
		{
			bytes_transferred = 0;

			return true;
		}

		sockaddr_in peer;

		int32 fd = socket::accept(_descriptor,&peer);

		_socket.set_fd(fd);

		_socket.set_peer(peer);

		return true;
	}

	void complete(Error err,size_t bytes_transferred)
	{
		_handler(err);
	}

};

template<typename Descriptor,typename BufferType,typename CompleteHandler>
class ReadOperation:public Operation
{
private:
	Descriptor _descriptor;
	BufferType & _buff;
	CompleteHandler _handler;
public:
	ReadOperation(Descriptor descriptor,BufferType & buff,const CompleteHandler & handler)
	:_descriptor(descriptor),_buff(buff),_handler(handler)
	{

	}

	bool perform(Error & err,size_t & bytes_transferred)
	{
		if(err.is_err() == true)
		{
			bytes_transferred = 0;

			return true;
		}

		int32 bytes = socket::read(_descriptor,_buff.read_data(),_buff.readable_size());

		int32 err_no = errno;



		return true;
	}

	void complete(Error err,size_t bytes_transferred)
	{
		_handler(err,bytes_transferred);
	}
};

template<typename Descriptor,typename BufferType,typename CompleteHandler>
class WriteOperation:public Operation
{
private:
	Descriptor _descriptor;
	BufferType & _buff;
	CompleteHandler _handler;
public:
	WriteOperation(Descriptor descriptor,BufferType & buff,const CompleteHandler & handler)
	:_descriptor(descriptor),_buff(buff),_handler(handler)
	{

	}

	bool perform(Error & err,size_t & bytes_transferred)
	{
		if(err.is_err())
		{
			return true;
		}

		return true;
	}

	void complete(Error err,size_t bytes_transferred)
	{
		_handler(err,0);
	}
};

class Operations
{
	struct Op
	{
		Op * _next;
		Operation * _operation;

		Error _err;
		size_t _byte_transferred;

		Op(Operation * operation) :
			 _next(NULL),_operation(operation), _byte_transferred(0)
		{

		}

		bool perform(const Error & err )
		{
			_err = err;

			if(err.is_err())
			{
				return true;
			}

			return _operation->perform(_err,_byte_transferred);
		}

		void complete()
		{
			_operation->complete(_err,_byte_transferred);
		}
	};

	typedef unordered_map<int32, Op *> OperationMap;

	OperationMap _operations;

	Op * _complete_opertions;

public:
	Operations() :
			_complete_opertions(NULL)
	{

	}

	void add_opertion(int32 fd,Operation * op)
	{
		Op * new_op = new Op(op);

		typedef OperationMap::iterator iterator;
		typedef OperationMap::value_type value_type;

		 std::pair<iterator, bool> entry =
				 _operations.insert(value_type(fd, new_op));

		if(entry.second)
		{
			return;
		}

		Op * curr_op = entry.first->second;

		while(curr_op->_next)
		{
			curr_op = curr_op->_next;
		}

		curr_op->_next = new_op;
	}

	bool empty()const
	{
		return _operations.empty();
	}

	bool has_operation(int32 fd)const
	{
		OperationMap::const_iterator it = _operations.find(fd);

		if(it == _operations.end())
		{
			return false;
		}

		return true;
	}

	bool perform(int32 fd,Error & err)
	{
		OperationMap::iterator it = _operations.find(fd);

		if(it == _operations.end())
		{
			return false;
		}

		Op * curr_op = it->second;
		Op * next_op = curr_op->_next;

		bool done = curr_op->perform(err) ;

		if(done)
		{
			curr_op->_next = _complete_opertions;

			_complete_opertions = curr_op;

			if(next_op)
			{
				it->second = next_op;

				return true;
			}

			_operations.erase(it);

			return false;
		}

		return true;
	}

	void complete()
	{
	    while (_complete_opertions)
	    {
	      Op * next_op = _complete_opertions->_next;
	      _complete_opertions->_next = NULL;
	      _complete_opertions->complete();

	      delete next_op;

	      _complete_opertions = next_op;
	    }
	}

	void perform_all_operations(int32 fd,const Error & err)
	{
		OperationMap::iterator it = _operations.find(fd);

		if (it == _operations.end())
		{
			return;
		}

		while (it->second)
		{
			Op * curr_op = it->second;
			Op * next_op = curr_op->_next;

			bool done = curr_op->perform(err);

			if (done)
			{
				curr_op->_next = _complete_opertions;

				_complete_opertions = curr_op;

				it->second = next_op;
			}
			else
			{
				return;
			}
		}
		_operations.erase(it);
	}

};

} /* namespace zeus */
#endif /* OPERATION_HPP_ */
